package encryption;

import Navi_utils.GeoHash;
import info.debatty.java.lsh.LSHMinHash;
import objects.LocationBean;
import org.ujmp.core.DenseMatrix;
import org.ujmp.core.Matrix;
//import com.google.common.hash.BloomFilter;


import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.security.SecureRandom;
import java.util.*;

import static parameter.parameter_length.vector_length;
import static utils.matrix.mathDeterminantCalculationValue;

public class Fuzzy_search {
    public static Matrix V;
    public static Matrix SK1;
    public static Matrix SK2;
    public static Matrix HKP;
    public static Matrix Q11;
    public static Matrix Q22;
    public static LSHMinHash lshMinHash = new LSHMinHash(100, 100, 36 * 36);


    // 生成模糊搜索密钥SK(SK1,SK2,V)
    public static Matrix generateSearchKey(int k1, int k2) {
        Random random = new Random();
        // 生成SK1和SK2
        SK1 = DenseMatrix.Factory.zeros(k1, k2);
        SK2 = DenseMatrix.Factory.zeros(k1, k2);
        for (int i = 0; i < k1; i++) {
            for (int j = 0; j < k2; j++) {
                SK1.setAsBoolean(random.nextBoolean(), i, j);
                SK2.setAsBoolean(random.nextBoolean(), i, j);
            }
        }
        V = DenseMatrix.Factory.rand(1, k2);
        for (int i = 0; i < k2; i++) {
            V.setAsBoolean(random.nextBoolean(), 0, i);
        }
        HKP = DenseMatrix.Factory.zeros(100, 100);
        for (int i = 0; i < 100; i++) {
            for (int j = 0; j < 100; j++) {
                HKP.setAsBoolean(random.nextBoolean(), i, j);
            }
        }
        return null;
    }

    // 生成可逆矩阵
    public static Matrix generateInvertMatrix(int k1, int k2) {
        Matrix temp_SK = DenseMatrix.Factory.zeros(k1, k2);
        SecureRandom random = new SecureRandom();
        while (true) {
            for (int i = 0; i < k1; i++) {
                for (int j = 0; j < k2; j++) {
                    temp_SK.setAsBoolean(random.nextBoolean(), i, j);
                }
            }
            double result = 0;
            try {
                result = mathDeterminantCalculationValue(temp_SK.toDoubleArray());
            } catch (Exception e) {
                e.printStackTrace();
            }
            if (result != 0) {
                break;
            }
        }
        return temp_SK;
    }

    // 对计算结果二元分词后进行goHash运算，已弃用
    public static double[] BuildIndex(LocationBean locationBean, byte[] key, LSHMinHash lshMinHash) {
        GeoHash geoHash = new GeoHash(locationBean);
        String temp = geoHash.getGeoHashBase32();
        double[] vector = new double[vector_length];
        //对输入进行二元分词并将对应向量位置置为1
        for (int i = 0; i < temp.length() - 1; i++) {
            //将字母转化为ASCII
            //求出要在vector中标记为1的位置
            //第一个字母所在位置*26+第二个字母所在位置,eg aa=0 bb=27 cc = 26*2+2 zz = 25*26+25
            int temp1 = 0;
            if (Character.isDigit(temp.charAt(i))) {
                temp1 = ((int) temp.charAt(i) - 48) * 36;
            } else {
                temp1 = ((int) temp.charAt(i) - 97 + 10) * 36;
            }
            int temp2 = 0;
            if (Character.isDigit(temp.charAt(i + 1))) {
                temp2 = ((int) temp.charAt(i + 1) - 48);
            } else {
                temp2 = ((int) temp.charAt(i + 1) - 97 + 10);
            }
            vector[temp1 + temp2] = 1;
        }
        //int[] Index = lshMinHash.hash(vector);
        return vector;
    }

    //对计算结果隐射到布隆过滤器中后建立索引，效率最高最好
    public static long[] BuildIndex(LocationBean locationBean, byte[] key) {
        GeoHash geoHash = new GeoHash(locationBean);
        String temp = geoHash.getGeoHashBase32();
        System.out.println(temp);
        BloomFilter bloomFilter = new BloomFilter(32);
        bloomFilter.addIfNotExist(temp);
        System.out.println(Arrays.toString(bloomFilter.notebook.toByteArray()));
        System.out.println(bloomFilter.notebook.toByteArray().length);
        long[] temp_long = bloomFilter.notebook.toLongArray();
        long[] tempresult = new long[16];
        for (int i = 0; i < temp_long.length; i++) {
            tempresult[i] = temp_long[i];
        }
        return tempresult;
    }

    //对计算结果隐射到布隆过滤器中后建立索引，效率最高最好
    public static byte[] BuildIndex(LocationBean locationBean) {
        GeoHash geoHash = new GeoHash(locationBean);
        String temp = geoHash.getGeoHashBase32();
        System.out.println(temp);
        BloomFilter bloomFilter = new BloomFilter(32);
        bloomFilter.addIfNotExist(temp);
        System.out.println(Arrays.toString(bloomFilter.notebook.toByteArray()));
        System.out.println(bloomFilter.notebook.toByteArray().length);
        byte[] temp_byte = bloomFilter.notebook.toByteArray();
        byte[] result = new byte[128];
        for (int i=0;i<temp_byte.length;i++){
            result[i] = temp_byte[i];
        }
        return null;
    }


    //通过S将I分割成I`和I``，然后用SK1和SK2加密
    //旧版本加密方式
    public static Map<String, Matrix> Enc_Index(Matrix SK1, Matrix SK2, Matrix V, double[] I) {
        double[] I1 = new double[I.length];
        double[] I2 = new double[I.length];
        Random random = new Random();
        for (int i = 0; i < V.getColumnCount(); i++) {
            if (V.getAsBoolean(0, i)) {
                //I`[t] = I``[t] =I[t]
                I1[i] = I2[i] = I[i];

            } else {
                //生成100以内的随机数
                int temp = random.nextInt(100);
                // 这里直接整数处以2等整数了，存在0.5情况导致信息丢失
                I1[i] = (double) I[i] / 2 + temp;
                I2[i] = (double) I[i] / 2 - temp;
            }
        }
        //求加密后的安全索引 EncIndex(I) = {SK1^T·I1,SK2^T·I2}
//        Matrix SK1_I1 = DenseMatrix.Factory.importFromArray(I1).mtimes(SK1.transpose());
//        Matrix SK2_I2 = DenseMatrix.Factory.importFromArray(I2).mtimes(SK2.transpose());
        Matrix SK1_I1 = SK1.transpose().mtimes(DenseMatrix.Factory.importFromArray(I1).transpose());
        Matrix SK2_I2 = SK2.transpose().mtimes(DenseMatrix.Factory.importFromArray(I2).transpose());
        // 将索引加密结果保存到EncIndex
        Map<String, Matrix> EncIndex = new HashMap<>();
        EncIndex.put("SK1_I1", SK1_I1);
        EncIndex.put("SK2_I2", SK2_I2);
        return EncIndex;
    }

    //通过S将I分割成I`和I``，然后用SK1和SK2加密
    //新版本加密方式
    public static Map<String, Matrix> Enc_Index(Matrix SK1, Matrix SK2, Matrix V, long[] I) {
        long[] I1 = new long[I.length];
        long[] I2 = new long[I.length];
        Random random = new Random();
        for (int i = 0; i < V.getColumnCount(); i++) {
            if (V.getAsBoolean(0, i)) {
                //I`[t] = I``[t] =I[t]
                I1[i] = I2[i] = I[i];

            } else {
                //生成100以内的随机数
                int temp = random.nextInt(100);
                // 这里直接整数处以2等整数了，存在0.5情况导致信息丢失
                I1[i] = I[i] / 2 + temp;
                I2[i] = I[i] / 2 - temp;
            }
        }
        Matrix SK1_I1 = SK1.transpose().mtimes(DenseMatrix.Factory.importFromArray(I1).transpose());
        Matrix SK2_I2 = SK2.transpose().mtimes(DenseMatrix.Factory.importFromArray(I2).transpose());
        // 将索引加密结果保存到EncIndex
        Map<String, Matrix> EncIndex = new HashMap<>();
        EncIndex.put("SK1_I1", SK1_I1);
        EncIndex.put("SK2_I2", SK2_I2);
        return EncIndex;
    }


    // 对查询向量进行加密
    //旧版本加密方式
    public static Map<String, Matrix> Enc_Query(Matrix SK1, Matrix SK2, Matrix V, double[] Q) {
        double[] Q1 = new double[Q.length];
        double[] Q2 = new double[Q.length];
        Random random = new Random();
        for (int i = 0; i < V.getColumnCount(); i++) {
            if (V.getAsBoolean(0, i)) {
                //生成100以内的随机数
                int temp = random.nextInt(100);
                // 这里直接整数处以2等整数了，存在0.5情况导致信息丢失
                Q1[i] = (double) Q[i] / 2 + temp;
                Q2[i] = (double) Q[i] / 2 - temp;
            } else {
                Q1[i] = Q2[i] = Q[i];
            }
        }
        Q11 = DenseMatrix.Factory.importFromArray(Q1);
        Q22 = DenseMatrix.Factory.importFromArray(Q2);
        Matrix SK1_Q1 = SK1.inv().mtimes(DenseMatrix.Factory.importFromArray(Q1).transpose());
        Matrix SK2_Q2 = SK2.inv().mtimes(DenseMatrix.Factory.importFromArray(Q2).transpose());
        // 将索引加密结果保存到EncIndex
        Map<String, Matrix> EncQuery = new HashMap<>();
        EncQuery.put("SK1_Q1", SK1_Q1);
        EncQuery.put("SK2_Q2", SK2_Q2);
        return EncQuery;
    }

    // 对查询向量进行加密
    //新版本加密方式
    public static Map<String, Matrix> Enc_Query(Matrix SK1, Matrix SK2, Matrix V, long[] Q) {
        long[] Q1 = new long[Q.length];
        long[] Q2 = new long[Q.length];
        Random random = new Random();
        for (int i = 0; i < V.getColumnCount(); i++) {
            if (V.getAsBoolean(0, i)) {
                //生成100以内的随机数
                int temp = random.nextInt(100);
                // 这里直接整数处以2等整数了，存在0.5情况导致信息丢失
                Q1[i] = Q[i] / 2 + temp;
                Q2[i] = Q[i] / 2 - temp;
            } else {
                Q1[i] = Q2[i] = Q[i];
            }
        }
        Q11 = DenseMatrix.Factory.importFromArray(Q1);
        Q22 = DenseMatrix.Factory.importFromArray(Q2);
        Matrix SK1_Q1 = SK1.inv().mtimes(DenseMatrix.Factory.importFromArray(Q1).transpose());
        Matrix SK2_Q2 = SK2.inv().mtimes(DenseMatrix.Factory.importFromArray(Q2).transpose());
        // 将索引加密结果保存到EncIndex
        Map<String, Matrix> EncQuery = new HashMap<>();
        EncQuery.put("SK1_Q1", SK1_Q1);
        EncQuery.put("SK2_Q2", SK2_Q2);
        return EncQuery;
    }

    // 对查询语句生成陷门
    // 旧版本生成方式
    public static Map<String, Matrix> trapDoor(Matrix SK1, Matrix SK2, Matrix HKP, Matrix V, LocationBean locationBean, LSHMinHash lshMinHash) {
        byte[] key = new byte[(int) HKP.getColumnCount()];
        for (int i = 0; i < key.length; i++) {
            key[i] = HKP.getAsByte(0, i);
        }
        double[] vector = BuildIndex(locationBean, key, lshMinHash);
        Map<String, Matrix> EncSK;
        EncSK = Enc_Query(SK1, SK2, V, vector);
        return EncSK;
    }

    // 对查询语句生成陷门
    // 新版本生成方式
    public static Map<String, Matrix> trapDoor(Matrix SK1, Matrix SK2, Matrix HKP, Matrix V, LocationBean locationBean) {
        byte[] key = new byte[(int) HKP.getColumnCount()];
        for (int i = 0; i < key.length; i++) {
            key[i] = HKP.getAsByte(0, i);
        }
        long[] vector = BuildIndex(locationBean, key);
        Map<String, Matrix> EncSK;
        EncSK = Enc_Query(SK1, SK2, V, vector);
        return EncSK;
    }

    //进行查询，计算安全索引向量Encsk(I)和陷门函数EncSK(Q)之间的内积
    public static void Search(Map<String, Matrix> EncIndex, Map<String, Matrix> EncSK) {
        Matrix eee = (EncIndex.get("SK1_I1").transpose()).mtimes(EncSK.get("SK1_Q1")).plus((EncIndex.get("SK2_I2").transpose()).mtimes(EncSK.get("SK2_Q2")));
        System.out.println("求得内积结果：" + eee.getAsLong(0, 0));
    }

    public static void main(String[] args) {

        String message = "baojishijintaiqu";
        String message2 = "baojishi";
        String message3 = "baojishijintaiqu";
        String message4 = "baojishjintaiqu";
        String message5 = "baojisjtaiqu";
        int k1, k2;
        k1 = k2 = 16;
        generateSearchKey(k1, k2);
        byte[] key = new byte[(int) HKP.getColumnCount()];
        //获取加密索引要用的密钥
        for (int i = 0; i < key.length; i++) {
            key[i] = HKP.getAsByte(0, i);
        }
        LocationBean locationBean = new LocationBean(46.9999999, 45.9999999);
        LocationBean locationBean1 = new LocationBean(46.9999998, 45.999997);
        LocationBean locationBean2 = new LocationBean(47.7777777, 47.7777777);
        LocationBean locationBean3 = new LocationBean(48.6666666, 48.6666666);
        LocationBean locationBean4 = new LocationBean(49.5555555, 49.5555555);
        //      double[] I = BuildIndex(locationBean, key, lshMinHash);
//        //加密后计算出来的内积  内积越高，相似度越高
//        Search(Enc_Index(SK1, SK2, V, I), trapDoor(SK1, SK2, HKP, V, locationBean1, lshMinHash));
//        Search(Enc_Index(SK1, SK2, V, I), trapDoor(SK1, SK2, HKP, V, locationBean2, lshMinHash));
//        Search(Enc_Index(SK1, SK2, V, I), trapDoor(SK1, SK2, HKP, V, locationBean3, lshMinHash));
//        Search(Enc_Index(SK1, SK2, V, I), trapDoor(SK1, SK2, HKP, V, locationBean4, lshMinHash));

        long[] test = BuildIndex(locationBean, key);
        //加密后计算出来的内积  内积越高，相似度越高

        for (int i = 0; i < 1000; i++) {
            locationBean1 = new LocationBean(locationBean1.getLat() + 0.00004, locationBean1.getLng() + 0.00004);
            Search(Enc_Index(SK1, SK2, V, test), trapDoor(SK1, SK2, HKP, V, locationBean1));
        }
        Search(Enc_Index(SK1, SK2, V, test), trapDoor(SK1, SK2, HKP, V, locationBean1));
        Search(Enc_Index(SK1, SK2, V, test), trapDoor(SK1, SK2, HKP, V, locationBean2));
        Search(Enc_Index(SK1, SK2, V, test), trapDoor(SK1, SK2, HKP, V, locationBean3));
        Search(Enc_Index(SK1, SK2, V, test), trapDoor(SK1, SK2, HKP, V, locationBean4));

    }
}
